﻿using FlyIL;
using FlyIL.Advance;
using FlyIL.Emu;
using FlyIL.Lifter;
using FlyIL.Misc;
using FlyRemote.Base;
using FlyRemote.Remote;
using System.Collections.Generic;
using System.Linq;
using static FlyIL.AssemblerRegisters;
using static FlyIL.OperandMaker;

namespace FuckVmp.VMP
{
    public class VMEntry : VMItem
    {

        Context _vminit;

        HandleDetecor Detecor64;

        byte Arch;


        IReader Reader;

        /// <summary>
        ///  vm 入口地址 ,即第一条添加的指令 
        /// </summary>
        public ulong Vip { get; private set; }

        /// <summary>
        /// 各handler 的防真 
        /// </summary>
        Dictionary<ulong, HandleInfo> Handlers = new Dictionary<ulong, HandleInfo>();


        /// <summary>
        /// 组装 vm_init  
        /// 并获取 两个关键的 寄存器位 
        /// </summary>
        public VMEntry(ReadGroup group, ulong address, IReader reader, Context vcontext)
        {

            RegCode codeIp = RegCode.None;
            RegCode codeSp = RegCode.None;

            Reader = reader;

            this.Entry = address;
            this.Group = group;

            this._vminit = vcontext;

            IEnumerable<Inst> reads = _vminit.Codes.Where(o => o.Opt == Opt.mov && o.O2.IsPtr && o.O2.Index == RegCode.None && o.O2.u32 == 0 && o.O2.PtrSize == 4);

            if (reads.Count() > 0)
            {
                codeIp = reads.First().O2.Base;

                this.Arch = (byte)codeIp.BitSize();

            }

            IEnumerable<Inst> save_vsp = _vminit.Codes.Where(o => o.Opt == Opt.mov && o.O2.IsRegister && o.O2.Base == (this.Arch == 64 ? RegCode.rsp : RegCode.esp));
            if (save_vsp.Count() > 0)
            {
                codeSp = save_vsp.First().O1.Base;
            }


            Detecor64 = new HandleDetecor(this.Arch, codeIp, codeSp);

        }
        public override string ToString()
        {
            return $"VMEntry => {Entry:X}";
        }



        /// <summary>
        /// 执行反编译 
        /// </summary>
        public Context DisVm()
        {
            Context vcon = new(Arch == 64, _vminit.Entry.Vip)
            {
                DumpPath = "C:\\Users\\lyo\\Desktop\\dump.txt"
            };


            var _vsp = Arch == 64 ? Rsp : Esp;

            var _vflag = Arch == 64 ? QFlag : EFlag;


            Cpu cpu = new Cpu(Arch == 64, Reader);


            cpu.Run(_vminit, _vminit.Entry);



            for (var node = _vminit.Entry; node != null;)
            {

                var inst = node;


                /// 将 vminit 添加到  vm代码中 ， 直到传递了 栈到虚拟栈 
                if (inst.Opt == Opt.mov && inst.O2.IsReg(Arch == 64 ? RegCode.rsp : RegCode.esp))
                    break;

                if (inst.Opt == Opt.call)
                {
                    if (Arch == 64)
                        vcon.Make().push(inst.Vip);
                    else
                        vcon.Make().push((uint)(inst.Vip));

                    node = _vminit[inst.O1.u64];
                    continue;
                }
                else if (inst.Opt == Opt.jmp)
                {
                    node = _vminit[inst.O1.u64];
                    continue;
                }
                else
                {
                    vcon.Make().CopyFrom(inst);
                    node = node.Next;
                }




                vcon.SimplifyOnce();

                //vcon.CheckCondition(myReader);
            }



            for (int i = 0; i < 1000; i++)
            {
                ulong next = cpu.NextIp;
                ulong vip;

                if (this.Arch == 0x64)
                    vip = cpu.GetReg64(Detecor64.RegVip);
                else
                    vip = cpu.GetReg32(Detecor64.RegVip);


                if (i == 0)
                {

                    //vcon.Make().mov( Operand.Make(Detecor64.RegVip) , 0 )  ;

                    var op = new Operand(Detecor64.RegVip);
                    vcon.RemoveDeadCode(vcon.Last, ref op);

                    vcon.Make().mov(new Operand(Detecor64.RegVsp), _vsp);
                    vcon.SimplifyOnce();

                    vcon.Make().jmp(vip);


                    /// vmint 只保留有效的 push 和 最后的 一个 jmp 
                    foreach (Inst u in vcon.Codes)
                    {
                        if (u.Opt != Opt.push && u.Opt != Opt.jmp)
                            u.nop();
                    }

                    vcon.ClearNop();
                    vcon.Dump();
                }



                if (!Handlers.ContainsKey(next))
                {

                    var lifter = new Lifters.Intel(Arch == 64, Reader);
                    Context con = new(Arch == 64, (next - Group.BaseAddress));
                    lifter.Collect(vcon, new Lifters.IntelOption { CallAsjmp = true, TrustJcc = true }, (next - Group.BaseAddress));

                    // var con = new MyLego(Arch, new SnapshotCell(Group, (int)(next - Group.BaseAddress), 0)).JointVTIL(null);

                    con.Dump();

                    var info = new HandleInfo(con);

                    Detecor64.CheckType(info);

                    Handlers[next] = info;
                }


                var data = Handlers[next];



                //Logger.Debug.Log($"第 {i} 条  {vip.ToString("X")} {data.Type} ");

                if (data.KeyInst == null)
                    cpu.Run(data.Context, data.Context.Entry);
                else
                    // cpu.Run(data.Context, data.Context.Entry, null, data.KeyInst.Vip, data.action);
                    cpu.Run(data.Context, data.Context.Entry, null, (c, con, inst) => { if (inst.Vip == data.KeyInst.Vip) data.action(c); });
                //lego.CallAsm64WithContext(data.asm, ref context   /* , i == 0x00000018 */);



                int reg_index;
                switch (data.Type)
                {


                    #region 数据移动 

                    case HandleType.pop_tmp8:
                        reg_index = (int)data.moniterValue;
                        vcon.Make(vip).pop(_qword_[Tsp + reg_index]);
                        break;
                    case HandleType.pop_tmp4:
                        reg_index = (int)data.moniterValue;
                        vcon.Make(vip).pop(_dword_[Tsp + reg_index]);
                        break;
                    case HandleType.pop_tmp2:
                        reg_index = (int)data.moniterValue;
                        vcon.Make(vip).pop(_word_[Tsp + reg_index]);
                        break;
                    case HandleType.pop_tmp1:
                        reg_index = (int)data.moniterValue;
                        vcon.Make(vip).pop(_byte_[Tsp + reg_index]);
                        break;

                    case HandleType.push_tmp8:
                        reg_index = (int)data.moniterValue;
                        vcon.Make(vip).push(_qword_[Tsp + reg_index]);
                        break;
                    case HandleType.push_tmp4:
                        reg_index = (int)data.moniterValue;
                        vcon.Make(vip).push(_dword_[Tsp + reg_index]);
                        break;
                    case HandleType.push_tmp2:
                        reg_index = (int)data.moniterValue;
                        vcon.Make(vip).push(_word_[Tsp + reg_index]);
                        break;
                    case HandleType.push_tmp1:
                        reg_index = (int)data.moniterValue;
                        vcon.Make(vip).push(_byte_[Tsp + reg_index]);
                        break;

                    case HandleType.push_imm8:
                        vcon.Make(vip).push((ulong)data.moniterValue);
                        break;
                    case HandleType.push_imm4:
                        vcon.Make(vip).push((uint)data.moniterValue);
                        break;
                    case HandleType.push_imm2:
                        vcon.Make(vip).push(data.moniterValue, 2);
                        break;
                    case HandleType.push_imm1:
                        vcon.Make(vip).push(data.moniterValue, 1);
                        break;

                    case HandleType.push_vsp:
                        vcon.Make(vip).push(_vsp);
                        break;

                    case HandleType.stack_xchg4:

                        if (Arch == 64)
                        {
                            // vcon.Make(vip, 1).xchg(_dword_[_vsp + 0xC], _dword_[_vsp]);
                        }
                        else
                        {
                            // vcon.Make(vip).xchg(_dword_[_vsp], _dword_[_vsp + 4]);
                        }
                        break;

                    #endregion

                    #region 栈计算 

                    case HandleType.stack_add8:
                        vcon.Make(vip).add(_qword_[Rsp + 8], _qword_[Rsp]);
                        vcon.Make(vip, 1).mov(_qword_[Rsp], QFlag);
                        break;

                    case HandleType.stack_add4:
                        vcon.Make(vip).add(_dword_[_vsp + 4], _dword_[_vsp]);
                        vcon.Make(vip, 1).mov(_dword_[_vsp], _vflag);
                        break;

                    case HandleType.stack_add2:
                        vcon.Make(vip).add(_word_[_vsp + 2], _word_[_vsp]);
                        vcon.Make(vip, 1).add(_vsp, 2);
                        vcon.Make(vip, 3).push(_vflag);
                        break;

                    case HandleType.not_or8:
                        vcon.Make(vip).not(_qword_[_vsp]);
                        vcon.Make(vip, 1).not(_qword_[_vsp + 8]);
                        vcon.Make(vip, 2).or(_qword_[_vsp + 8], _qword_[_vsp]);
                        vcon.Make(vip, 3).mov(_qword_[_vsp], QFlag);
                        break;


                    case HandleType.not_or4:
                        vcon.Make(vip).not(_dword_[_vsp]);
                        vcon.Make(vip, 1).not(_dword_[_vsp + 4]);
                        vcon.Make(vip, 2).or(_dword_[_vsp + 4], _dword_[_vsp]);
                        vcon.Make(vip, 3).mov(_dword_[_vsp], _vflag);

                        break;

                    case HandleType.not_and8:
                        vcon.Make(vip).not(_qword_[_vsp]);
                        vcon.Make(vip, 1).not(_qword_[_vsp + 8]);
                        vcon.Make(vip, 2).and(_qword_[_vsp + 8], _qword_[_vsp]);
                        vcon.Make(vip, 3).mov(_qword_[_vsp], _vflag);

                        break;


                    case HandleType.not_and4:
                        vcon.Make(vip).not(_dword_[_vsp]);
                        vcon.Make(vip, 1).not(_dword_[_vsp + 4]);
                        vcon.Make(vip, 2).and(_dword_[_vsp + 4], _dword_[_vsp]);
                        vcon.Make(vip, 3).mov(_dword_[_vsp], _vflag);

                        break;

                    case HandleType.read8:
                        vcon.Make(vip).mov(R0, _qword_[_vsp]);
                        vcon.Make(vip, 1).mov(_qword_[_vsp], _qword_[R0]);
                        break;

                    case HandleType.read4:
                        if (this.Arch == 64)
                        {
                            vcon.Make(vip).mov(R0, _qword_[_vsp]);
                            vcon.Make(vip, 1).mov(_dword_[_vsp], _dword_[R0]);
                        }
                        else
                        {

                            if (vcon.Codes.Last.Opt == Opt.push)
                            {
                                if (vcon.Codes.Last.O1 == _regs_[Esp])
                                {

                                    vcon.Codes.Last.push(_dword_[Esp]);
                                    vcon.Make(vip).nop();
                                }
                                else if (vcon.Codes.Last.O1.IsImm)
                                {
                                    vcon.Codes.Last.push(_dword_[vcon.Codes.Last.O1.u64]);
                                    vcon.PassConstHeapAccess(vcon.Last);
                                    vcon.Make(vip).nop();
                                }
                            }
                            else
                            {
                                vcon.Make(vip).mov(R0D, _dword_[_vsp]);
                                vcon.Make(vip, 1).mov(_dword_[_vsp], _dword_[R0D]);
                            }
                        }
                        break;
                    case HandleType.read2:
                        if (this.Arch == 64)
                        {
                            vcon.Make(vip).mov(R0, _qword_[_vsp]);
                            vcon.Make(vip, 1).mov(_word_[_vsp], _word_[R0]);
                        }
                        else
                        {
                            vcon.Make(vip).mov(R0D, _dword_[_vsp]);
                            vcon.Make(vip, 1).mov(_word_[_vsp], _word_[R0D]);
                        }
                        break;
                    case HandleType.read1:

                        if (this.Arch == 64)
                        {
                            vcon.Make(vip).mov(R0, _qword_[_vsp]);
                            vcon.Make(vip, 1).mov(_byte_[_vsp], _byte_[R0]);
                        }
                        else
                        {
                            vcon.Make(vip).mov(R0, _dword_[_vsp]);
                            vcon.Make(vip, 1).mov(_byte_[_vsp], _byte_[R0]);
                        }
                        break;

                    case HandleType.shr4:

                        if (this.Arch == 64)
                        {
                            ;
                        }
                        else
                        {
                            vcon.Make(vip).mov(R0D, _dword_[_vsp]);
                            vcon.Make(vip, 1).shr(R0D, _byte_[_vsp + 4]);
                            vcon.Make(vip, 2).add(_vsp, 2);
                            vcon.Make(vip, 3).mov(_dword_[_vsp], R0D);
                            vcon.Make(vip, 4).push(_vflag);

                        }


                        break;

                    #endregion




                    case HandleType.cpuid:
                        if (Arch == 64)
                        {

                        }
                        else
                        {
                            vcon.Make(vip).emit("cpuid", null);
                            vcon.Make(vip, 1).push(R1D);
                            vcon.Make(vip, 2).push(R2D);
                            vcon.Make(vip, 3).push(R3D);
                            vcon.Make(vip, 4).push(R4D);

                        }
                        break;

                    case HandleType.ret:
                        vcon.Make(vip).ret();
                        break;


                    case HandleType.push_sp8:
                        vcon.Make(vip).push(Rsp);
                        break;
                    case HandleType.push_sp4:
                        vcon.Make(vip).push(Esp);
                        break;


                    default:
                        throw new System.Exception();


                }




                vcon.SimplifyOnce();

                vcon.ClearNop();
                vcon.Dump(false, vcon.Last.Vip);

                if (vcon.Last.Opt == Opt.ret)
                {

                    vcon.Save("d:\\codes.il");

                    vcon.SimplifyOnce(vcon.Last);

                    vcon.Dump();

                }
            }

            return vcon;
        }


    }
}
